package com.cougil.uoc.seghismed.business.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import com.cougil.uoc.seghismed.business.GestorService;
import com.cougil.uoc.seghismed.business.MetgeService;
import com.cougil.uoc.seghismed.business.PacientService;
import com.cougil.uoc.seghismed.business.VisitaService;
import com.cougil.uoc.seghismed.model.Historial;
import com.cougil.uoc.seghismed.model.Metge;
import com.cougil.uoc.seghismed.model.Pacient;
import com.cougil.uoc.seghismed.model.Usuari;
import com.cougil.uoc.seghismed.model.Visita;
import com.cougil.uoc.seghismed.model.ciphed.HistorialCiphed;
import com.cougil.uoc.seghismed.model.ciphed.MetgeCiphed;
import com.cougil.uoc.seghismed.model.ciphed.PacientCiphed;
import com.cougil.uoc.seghismed.model.ciphed.UsuariCiphed;
import com.cougil.uoc.seghismed.model.ciphed.VisitaCiphed;

/**
 * Classe que implementa els principals mètodes oferts a les peticions rebudes a través dels
 * diversos aplicatius, tant Metge com Pacient. Entre d'altres disposa de la lògica de negoci comuna
 * a ambdòs aplicacions i serveis web oferts
 *
 * @author Ignacio Cougil Jares
 *
 */
public class GestorServiceImpl extends CoreServerServiceImpl implements GestorService {

	private MetgeService metgeService;
	private PacientService pacientService;
	private VisitaService visitaService;

	public GestorServiceImpl() {

	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.GestorService#afegirVisita(com.cougil.uoc.seghismed.model.ciphed.MetgeCiphed, com.cougil.uoc.seghismed.model.ciphed.PacientCiphed, com.cougil.uoc.seghismed.model.ciphed.VisitaCiphed)
	 */
	@Override
	public void afegirVisita(MetgeCiphed metgeCiphed, PacientCiphed pacientCiphed, VisitaCiphed visitaCiphed) {

		// desxifrem l'usuari destinatari
		Visita visita = this.cipherService.verificarDesxifrarVisita(visitaCiphed, this.getGestor());
		Pacient pacient = this.cipherService.desxifrarPacient(pacientCiphed, this.getGestor());
		Metge metge = this.cipherService.desxifrarMetge(metgeCiphed, this.getGestor());

		// obtenim les dades del metge
		metge = this.metgeService.findById(metge.getId());
		visita.setMetge(metge);

		CoreServerServiceImpl.logger.debug("Intentant emmagatzemar la nova visita [" + visita
				+ "] al pacient [" + pacient + "] realitzat pel metge [" + metge + "]");

		// obtenim les dades del pacient amb l'historial xifrat pel gestor
		pacient = this.pacientService.findById(pacient.getId());

		// emmagatzemem la visita
		this.visitaService.save(visita);
		// cerquem la visita xifrada recentment creada
		VisitaCiphed vc = this.visitaService.findVisitaCiphed(visita);

		// l'afegim al conjunt de visites del pacient
		Historial h = pacient.getHistorial();
		h.addVisita(vc);

		// int randomId = SeghismedUtils.getRandomInt();
		// visita.setId(randomId);
		// visitaCiphed = this.cipherService.signarXifrarVisita(visita, this.getGestor(),
		// this.getGestor());
		// h.addVisita(visitaCiphed);

		// i finalment guardem el pacient, per tal que es guardi el seu historial amb la visita
		// xifrada associada
		this.pacientService.save(pacient);

		CoreServerServiceImpl.logger.debug("Visita ["+visita+"] emmagatzemada satisfactoriament");
	}

	/**
	 * Omple l'historial xifrat informat amb les dades dels metges amb accés protegit contingut,
	 * xifrant-ho només per al destinatari informat
	 *
	 * @param destinatari
	 * @param hCiphed
	 * @param h
	 */
	private void carregarMetgesAmbAccesProtegit(Usuari destinatari, HistorialCiphed hCiphed, Historial h) {
		for (MetgeCiphed metgeCiphed : h.getMetges()) {
			// en aquest instant el gestor pot desxifrar la informació donat que va xifrada només
			// per a ell
			Metge m = this.cipherService.desxifrarMetge(metgeCiphed, this.getGestor());
			// cerquem la seva informació
			m = this.metgeService.findById(m.getId());
			// el xifrem per l'usuari destinatari
			MetgeCiphed mc = this.cipherService.xifrarMetge(m, this.getGestor(), destinatari);
			// el fiquem al mapa corresponent amb la seva data associada
			Date data = h.getMetgesAmbAccesProtegit().get(metgeCiphed);
			// associem finalment a l'historial el metge i la seva data associada
			hCiphed.putMetge(mc, data);
		}
	}

	/**
	 * Omple el conjunt de pacients xifrats amb les dades dels pacients protegits, xifrant-ho només
	 * per al destinatari informat
	 */
	private void carregarPacientsProtegits(List<PacientCiphed> pacientsProtegits,
			List<PacientCiphed> pacientsARetornar, Usuari destinatari) {
		for (PacientCiphed pacientCiphed : pacientsProtegits) {
			// en aquest instant el gestor pot desxifrar la informació donat que va xifrada només
			// per a ell
			Pacient p = this.cipherService.desxifrarPacient(pacientCiphed, this.getGestor());
			// cerquem la seva informació
			p = this.pacientService.findById(p.getId());
			// el xifrem per l'usuari destinatari
			PacientCiphed pc = this.cipherService.xifrarPacient(p, this.getGestor(), destinatari);
			// l'associem finalment a la llista de pacients protegits
			pacientsARetornar.add(pc);
		}
	}

	/**
	 * Omple l'historial xifrat informat amb les dades de les visites protegides contingudes,
	 * signades i xifrades només per al destinatari informat
	 *
	 * @param destinatari
	 * @param h
	 * @param ciphed
	 */
	private void carregarVisitesProtegides(Usuari destinatari, HistorialCiphed hCiphed, Historial h) {
		for (VisitaCiphed visitaCiphed : h.getVisitesProtegides()) {
			// verifiquem i desxifrem la visita
			Visita v = this.cipherService.verificarDesxifrarVisita(visitaCiphed, this.getGestor());
			// cerquem la seva informació
			v = this.visitaService.findById(v.getId());
			// la xifrem i signem pel seu destinatari real
			VisitaCiphed vc = this.cipherService.signarXifrarVisita(v, this.getGestor(), destinatari);
			// la fiquem al conjunt de visites corresponent
			hCiphed.addVisita(vc);
		}
	}

	/**
	 * Cerca i retorna la visita informada, xifrada i signada per al destinatari indicat sempre que
	 * aquesta formi part del conjunt de visites associades al pacient facilitat. Sinó retorna null
	 */
	private VisitaCiphed cercarISignarVisitaProtegida(PacientCiphed pacientCiphed, Usuari destinatari,
			Visita visita) {

		VisitaCiphed vc = null;

		// obtenim l'historial xifrat pel gestor, que només conté les referències als objectes xifrats
		Historial h = this.pacientService.consultarHistorialXifratPelGestor(pacientCiphed);

		// agafem les seves visites...
		Set<VisitaCiphed> visitesCiphed = h.getVisitesProtegides();

		// ... i si trobem la visita xifrada dintre del conjunt de visites del pacient, serà correcte
		vc = this.visitaService.findVisitaCiphed(visitesCiphed, visita);

		if (vc != null) {

			// cerquem la seva informació
			visita = this.visitaService.findById(visita.getId());

			// la xifrem i signem pel seu destinatari real
			vc = this.cipherService.signarXifrarVisita(visita, this.getGestor(), destinatari);

		}

		return vc;
	}

	private PacientCiphed cercarPacientDelMetge(MetgeCiphed metgeCiphed, Pacient pacient) {

		// obtenim els pacients xifrats
		List<PacientCiphed> pacientsXifrats = this.metgeService.consultarPacients(metgeCiphed);

		// i cerquem el pacient xifrat dintre del seu conjunt de pacients xifrats
		return this.pacientService.findPacientCiphed(pacientsXifrats, pacient);

	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * com.cougil.uoc.seghismed.business.GestorService#consultarHistorial(com.cougil.uoc.seghismed
	 * .model.ciphed.PacientCiphed, com.cougil.uoc.seghismed.model.ciphed.UsuariCiphed)
	 */
	public HistorialCiphed consultarHistorial(PacientCiphed pacientCiphed, UsuariCiphed usuariCiphed) {

		// desxifrem l'usuari destinatari
		Usuari destinatari = this.cipherService.desxifrarUsuari(usuariCiphed, this.getGestor());

		// obtenim l'historial xifrat pel gestor, que només conté les referències als objectes xifrats
		Historial h = this.pacientService.consultarHistorialXifratPelGestor(pacientCiphed);

		// instanciem l'objecte de l'historial xifrat que retornarem
		HistorialCiphed hCiphed = new HistorialCiphed();

		// a continuació consultem tota la informació de l'historial per tal d'omplir totes les seves dades
		this.carregarMetgesAmbAccesProtegit(destinatari, hCiphed, h);
		this.carregarVisitesProtegides(destinatari, hCiphed, h);

		return hCiphed;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * com.cougil.uoc.seghismed.business.GestorService#consultarPacient(com.cougil.uoc.seghismed
	 * .model.ciphed.MetgeCiphed, com.cougil.uoc.seghismed.model.ciphed.PacientCiphed)
	 */
	@Override
	public PacientCiphed consultarPacient(MetgeCiphed metgeCiphed, PacientCiphed pacientCiphed) {

		// desxifrem el pacient
		Pacient pacient = this.cipherService.desxifrarPacient(pacientCiphed, this.getGestor());

		// i el cerquem dintre del seu propi conjunt de pacients
		PacientCiphed pc = this.cercarPacientDelMetge(metgeCiphed, pacient);

		// finalment consultem tota la seva informació, si és que hem trobat el pacient
		if (pc != null) {

			// cerquem la seva informació
			pacient = this.pacientService.findById(pacient.getId());

			// desxifrem el metge
			Metge metge = this.cipherService.desxifrarMetge(metgeCiphed, this.getGestor());

			// el xifrem pel seu destinatari real
			pc = this.cipherService.xifrarPacient(pacient, this.getGestor(), metge);

		} else {
			CoreServerServiceImpl.logger.error("El pacient [" + pacient + "] que es pretenia consultar "
					+ "no s'ha pogut trobar dintre del conjunt de pacients dels metge");
		}

		return pc;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.GestorService#consultarPacients(com.cougil.uoc.seghismed.model.ciphed.MetgeCiphed)
	 */
	@Override
	public List<PacientCiphed> consultarPacients(MetgeCiphed metgeCiphed) {

		// desxifrem l'usuari destinatari
		Metge destinatari = this.cipherService.desxifrarMetge(metgeCiphed, this.getGestor());

		// obtenim el conjunt de pacients xifrats només per al gestor, que només conté les referències als objectes
		// xifrats
		List<PacientCiphed> pacientsGestorProtegits = this.metgeService.consultarPacients(metgeCiphed);

		// instanciem l'objecte amb el conjunt de pacients xifrats que retornarem
		List<PacientCiphed> pacientsARetornar = new ArrayList<PacientCiphed>();

		// a continuació consultem tota la informació dels pacients per tal d'omplir totes les seves
		// dades
		this.carregarPacientsProtegits(pacientsGestorProtegits, pacientsARetornar, destinatari);

		return pacientsARetornar;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * com.cougil.uoc.seghismed.business.GestorService#consultarVisita(com.cougil.uoc.seghismed.
	 * model.ciphed.UsuariCiphed, com.cougil.uoc.seghismed.model.ciphed.PacientCiphed,
	 * com.cougil.uoc.seghismed.model.ciphed.VisitaCiphed)
	 */
	@Override
	public VisitaCiphed consultarVisita(UsuariCiphed usuariCiphed, PacientCiphed pacientCiphed,
			VisitaCiphed visitaCiphed) {

		VisitaCiphed vc = null;

		// desxifrem l'usuari destinatari
		Usuari destinatari = this.cipherService.desxifrarUsuari(usuariCiphed, this.getGestor());
		Visita visita = this.cipherService.verificarDesxifrarVisita(visitaCiphed, this.getGestor());

		// cerquem la visita protegida dintre del conjunt de visites xifrades i signades pel gestor
		vc = this.cercarISignarVisitaProtegida(pacientCiphed, destinatari, visita);

		if (vc == null) {
			if (destinatari instanceof Pacient) {
				CoreServerServiceImpl.logger.error("La visita [" + visita + "] que es pretenia consultar "
						+ "no s'ha pogut trobar dintre del conjunt de visites del pacient");

			// sinó és metge
			} else {
				CoreServerServiceImpl.logger.error("La visita [" + visita + "] que es pretenia consultar "
						+ "no s'ha pogut trobar dintre del conjunt de visites dels pacients del metge");
			}
		}

		/*
		 * // si és pacient, cal comprovar que la visita és seva if (destinatari instanceof Pacient)
		 * {
		 *
		 * // cerquem la visita protegida dintre del conjunt de visites xifrades i signades pel //
		 * gestor vc = this.cercarISignarVisitaProtegida(pacientCiphed, destinatari, visita);
		 *
		 * if (vc == null) { CoreServerServiceImpl.logger.error("La visita [" + visita +
		 * "] que es pretenia consultar " +
		 * "no s'ha pogut trobar dintre del conjunt de visites del pacient"); }
		 *
		 * // sinó, és metge: o bé ha d'ésser d'un dels seus pacients o bé ha d'ésser un dels metges
		 * amb accés protegit } else if (destinatari instanceof Metge) {
		 *
		 * // obtenim tots els pacients del metge, xifrats per al gestor List<PacientCiphed>
		 * pacients = this.metgeService.consultarPacients((MetgeCiphed) usuariCiphed);
		 *
		 * // per cada pacient, comprovem si la visita en qüestió és seva boolean trobada = false;
		 * for (Iterator<PacientCiphed> iterator = pacients.iterator(); iterator.hasNext() &&
		 * !trobada;) { PacientCiphed pc = iterator.next(); vc =
		 * this.cercarISignarVisitaProtegida(pc, destinatari, visita); trobada = (vc != null); }
		 *
		 * // cerquem la visita protegida dintre del conjunt de visites xifrades i signades pel //
		 * gestor vc = this.cercarISignarVisitaProtegida(pacientCiphed, destinatari, visita);
		 *
		 * if (vc == null) { CoreServerServiceImpl.logger.error("La visita [" + visita +
		 * "] que es pretenia consultar " +
		 * "no s'ha pogut trobar dintre del conjunt de visites dels pacients del metge"); } }
		 */

		return vc;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.GestorService#eliminarMetge(com.cougil.uoc.seghismed.model.Metge)
	 */
	public void eliminarMetge(Metge metge) {

		// eliminem el metge
		this.metgeService.remove(metge.getId());

	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.GestorService#eliminarPacient(com.cougil.uoc.seghismed.model.Pacient)
	 */
	public void eliminarPacient(Pacient pacient) {

		// eliminem en primera instància el conjunt de visites protegides associades a l'historial
		this.visitaService.remove(pacient.getHistorial().getVisitesProtegides());

		// finalment, eliminem el pacient (i el seu historial associat)
		this.pacientService.remove(pacient.getId());

	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.GestorService#crearMetge(com.cougil.uoc.seghismed.model.Metge)
	 */
	public Metge guardarMetge(Metge metge) {
		CoreServerServiceImpl.logger.debug("Metge rebut: "+metge);
		this.metgeService.save(metge);
		CoreServerServiceImpl.logger.debug("Metge retornat: "+metge);
		return metge;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.GestorService#crearPacient(com.cougil.uoc.seghismed.model.Pacient)
	 */
	public Pacient guardarPacient(Pacient pacient) {
		CoreServerServiceImpl.logger.debug("Pacient rebut: "+pacient);
		// no cal crear l'historial pel pacient, perquè ja es crea automàticament en instanciar l'objecte Pacient
		this.pacientService.save(pacient);
		CoreServerServiceImpl.logger.debug("Pacient retornat: "+pacient);
		return pacient;
	}

	/**
	 * @param Estableix metgeService com a valor de la propietat metgeService
	 */
	public void setMetgeService(MetgeService metgeService) {
		this.metgeService = metgeService;
	}

	/**
	 * @param Estableix pacientService com a valor de la propietat pacientService
	 */
	public void setPacientService(PacientService pacientService) {
		this.pacientService = pacientService;
	}

	/**
	 * @param Estableix
	 *            visitaService com a valor de la propietat visitaService
	 */
	public void setVisitaService(VisitaService visitaService) {
		this.visitaService = visitaService;
	}



}
